refactor: extract the source study citation into its own component (#3218) - #3224
refactor: extract the source study citation into its own component (#3218)#3224frano-m wants to merge 2 commits into
Conversation
7445807 to
e3ed9ad
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
splitTrailingWord currently slices the untrimmed input after computing an index from trimEnd(), which can produce a tail that includes trailing whitespace and contradicts the function’s documented behavior.
Pull request overview
This PR refactors the FileNameCell citation rendering by extracting the DOI/link + nowrap/icon logic into a dedicated Citation subcomponent, aligning the guard/narrowing behavior with publicationString and keeping the split work scoped to the DOI branch.
Changes:
- Updated
FileNameCellto guard onpublicationStringand delegate citation rendering to a new<Citation />component. - Introduced
Citationcomponent with supporting constants, styles, types, andsplitTrailingWordutility colocated underFileNameCell/components/Citation/. - Moved the DOI/link-related styles and constants out of
fileNameCell.tsxinto the citation-specific folder for better separation of concerns.
File summaries
| File | Description |
|---|---|
| components/HCABioNetworks/Network/Atlas/components/SourceDatasets/components/MainColumn/components/table/components/FileNameCell/fileNameCell.tsx | Simplifies cell rendering and delegates citation logic to <Citation />. |
| components/HCABioNetworks/Network/Atlas/components/SourceDatasets/components/MainColumn/components/table/components/FileNameCell/components/Citation/citation.tsx | New component encapsulating DOI-link citation rendering and icon/nowrap behavior. |
| components/HCABioNetworks/Network/Atlas/components/SourceDatasets/components/MainColumn/components/table/components/FileNameCell/components/Citation/citation.styles.ts | Moves and scopes citation-specific styled components (nowrap span + icon styling). |
| components/HCABioNetworks/Network/Atlas/components/SourceDatasets/components/MainColumn/components/table/components/FileNameCell/components/Citation/constants.ts | Centralizes DOI base URL and external-link accessibility title. |
| components/HCABioNetworks/Network/Atlas/components/SourceDatasets/components/MainColumn/components/table/components/FileNameCell/components/Citation/types.ts | Defines props contract for the extracted Citation component. |
| components/HCABioNetworks/Network/Atlas/components/SourceDatasets/components/MainColumn/components/table/components/FileNameCell/components/Citation/utils.ts | Houses splitTrailingWord used to keep the last word + icon unbroken. |
Review details
- Files reviewed: 3/6 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Good catch — real bug, fixed in bd8a436. The index came from That matters because the tail sits immediately before the icon inside the nowrap span, so it renders as a gap wider than the icon's deliberate Fixed by slicing the trimmed value, so const trimmed = text.trimEnd();
const index = trimmed.lastIndexOf(" ");
if (index === -1) return { head: "", tail: trimmed };
return { head: trimmed.slice(0, index + 1), tail: trimmed.slice(index + 1) };Compared old against new across the cases — only trailing-whitespace inputs change, every real citation is byte-identical:
Rendered output confirms it: the six distinct tails are Worth noting the whitespace path is unexercised by live data — no Breast v1.0 citation has trailing whitespace — so this was latent rather than visible. It is also exactly the kind of edge case the earlier review flagged as deserving unit tests for this function; there is still no test tooling in the repo (#3190), so I verified by executing the function directly rather than adding a test that cannot run. 🤖 Generated with Claude Code |
…3218) Filenamecell guarded the citation on the split result rather than on publicationstring, so the guard read indirectly, typescript stopped narrowing the string the no-doi branch renders, and the split ran on rows that never used it. A citation component taking a required publicationstring puts all three right: the caller owns whether there is a citation, the string arrives narrowed, and the split sits in the doi branch that consumes it. The citation-only pieces move with it, so filenamecell is now a stack and two lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The index came from trimend() but the slices came from the original, so any trailing whitespace landed on the tail - which sits immediately before the external-link icon, rendering as a gap wider than the icon's deliberate 4px margin. Also contradicted the jsdoc, which promises the final word. Only trailing-whitespace inputs change; every real citation is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bd8a436 to
3642595
Compare
NoopDog
left a comment
There was a problem hiding this comment.
Approving — the extraction is clean, callers all resolve, tsc --noEmit and npm run lint pass, and the rendered output is unchanged for the current data.
Two low-priority notes, neither blocking:
1. Citation/utils.ts — lastIndexOf(" ") only matches U+0020.
A citation whose word separators are non-breaking spaces, tabs, or newlines (plausible for a publicationString pasted from a typeset journal page) returns -1, so head is "" and the whole citation lands inside StyledNoWrap (white-space: nowrap). In the pinned Source Datasets column that overflows/clips the cell instead of wrapping to a second line — the opposite of what the helper exists to prevent. Pre-existing behaviour, but since the line was rewritten here it's cheap to close: match any whitespace, e.g. /\s(?=\S*$)/.
2. The PR description undersells one hunk.
The "Not done" section says splitTrailingWord "moved unchanged", but 3642595 slices tail from text.trimEnd(), so head + tail === text.trimEnd() rather than text. The change is correct and a genuine improvement — a trailing space inside a nowrap span renders as a visible gap before the icon — it just isn't part of the 100%-similarity renames, and a reviewer skimming that claim would skip the one hunk with behaviour in it.
🤖 Generated with Claude Code
Summary
Closes #3218.
FileNameCellguarded its citation line on the result of the split rather than on the citation itself. #3218 raised three consequences and offered the shape that fixes all three: a subcomponent taking a requiredpublicationString: string.The three points, and how each is now closed
{citation && …}guarded a block rendering barepublicationString{publicationString && …}— the thing itselfpublicationStringstayedstring | nullinside the guardstring, and is passed as a required propif (!doi) return …sits above the splitPoint 2 demonstrated rather than asserted: changing the call site to
publicationString.trimEnd()now compiles with no null assertion, where before it producedTS18047: 'publicationString' is possibly 'null'.The split is still called once per render, so #3213's fix is preserved rather than undone.
Structure
The citation-only pieces moved with it, so the concern is self-contained and
fileNameCell.tsxis back to aStackplus two lines:All three moves are 100%-similarity renames, so history follows them. Folder naming matches the sibling
AnalysisPortalCellconvention.Verification
Rendered output is unchanged:
<svg>(opens in a new tab)titles — thetitleAccessa11y cue surviveshref=""count 0var(--palette-ink-light); icon still 16pxHCA Explorerlinksnpm run lint(0 errors),npm run check-format,npx tsc --noEmit,npm run build-prod:data-portal(64/64) all passOne equivalence worth spelling out, raised in review: for
publicationString === ""the old guard producednulland rendered nothing; the new guard is falsy and also renders nothing. Equivalent for every value ofstring | null.Not done
splitTrailingWordis a pure function with edge cases that deserve unit tests — single word, trailing whitespace, empty string — and has none. It moved unchanged, so this is pre-existing, and there is still no test tooling in the repo (#3190). Worth seeding alongsidebuildVersionedFileNameandsplitFileNamewhen that lands.No conflict with the open PRs
Checked before starting: this touches only
FileNameCell/. #3223 touches@types/network.ts,constants/networks.tsand twocontent/breast/files; #3216 touchesviewModelBuilders.tsx. No overlap either way, so this sits onmainrather than stacking.🤖 Generated with Claude Code